﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RpcGenerator.CSharp;

namespace RpcGenerator
{
    class InvokerGenerator
    {
        public static void Generate(Type type, IMessageSerializer serializer, List<MessageDescription> allDescriptions,MessageFormatType format)
        {
            string invokerName = string.Format("{0}_Invoker", type.Name);
            serializer.NewLine(string.Format("public class {0} : RpcInvoker", invokerName));
            serializer.BeginBlock();
            serializer.NewLine(string.Format("public static readonly {0} Instance = new {0}();", invokerName));

            serializer.NewLine();
            GenerateInvoke(type, serializer, allDescriptions, format);

            serializer.NewLine();
            GenerateReturn(type, serializer, allDescriptions,format);

            serializer.EndBlock();

            string result = serializer.ToString();
            Console.WriteLine(result);
        }

        private static void GenerateInvoke(Type type, IMessageSerializer serializer, List<MessageDescription> allDescriptions, MessageFormatType format)
        {
            serializer.NewLine("public void Invoke(object _interface, RpcSessionService service, ushort flags, short methodid,Int32 OpCode,NetStream stream)");
            serializer.BeginBlock();
            serializer.NewLine(string.Format("{0} _implement = _interface as {0};", type.Name));
            serializer.NewLine(string.Format("{0}Asyn _implement_asyn = _interface as {0}Asyn;", type.Name));
            serializer.NewLine("switch (methodid)");
            serializer.BeginBlock();
            MessageDescriptionGenerator mdgenerator = new MessageDescriptionGenerator();
            allDescriptions = allDescriptions.Where(description => description.IsRequest == true).ToList();
            allDescriptions.ForEach(description =>
            {
                serializer.NewLine(string.Format("// MessageId = {0}:", description.MessageId));
                serializer.NewLine(string.Format("case {0}:", description.MethodId));
                serializer.BeginBlock();
                {
                    if (format == MessageFormatType.Json)
                    {
                        serializer.NewLine(string.Format("{0} request = {0}.ReadFromJson(stream);", description.Name));
                        serializer.NewLine("request.Read(stream);");
                    }
                    else
                    {
                        serializer.NewLine(string.Format("{0} request = new {0}();", description.Name));
                        serializer.NewLine("request.Read(stream);");
                    }    

                    if (description.Response != null)
                    {
                        if (description.Response.AsynMethod)
                        {
                            serializer.NewLine(string.Format("{0}AsynCallback callback = new {0}AsynCallback(service,{1},OpCode);", description.Method.Method.Name, description.NeedEncrypt ? "true" : "false"));
                            serializer.NewLine(string.Format("_implement_asyn.{0}(", description.Method.Method.Name));
                            description.Fields.ForEach(fieldDescription => 
                            {
                                serializer.Apppend(string.Format("request.{0},", fieldDescription.Name));
                            });
                            serializer.Apppend("callback.Return");
                            serializer.Apppend(");");
                        }
                        else
                        {
                            serializer.NewLine(string.Format("{0} response = new {0}();", description.Response.Name));
                            serializer.NewLine(string.Format("_implement.{0}(", description.Method.Method.Name));
                            if (description.Fields.Count > 0)
                            {
                                MessageFieldDescription lastField = description.Fields[description.Fields.Count - 1];
                                description.Fields.ForEach(fieldDescription =>
                                {
                                    if (lastField != fieldDescription)
                                    {
                                        serializer.Apppend(string.Format("request.{0},", fieldDescription.Name));
                                    }
                                    else
                                    {
                                        serializer.Apppend(string.Format("request.{0}", fieldDescription.Name));
                                    }
                                });
                            }
                            serializer.Apppend(");");
                            if (description.Response != null && description.Response.AsynMethod == false)
                            {
                                serializer.NewLine(string.Format("service.FromServiceReturn(response,{0},OpCode);", description.NeedEncrypt ? "true" : "false"));
                            }
                        }
                    }

                    serializer.NewLine("break;");
                }   
                serializer.EndBlock();
            });
            serializer.EndBlock();
            serializer.EndBlock();
        }

        private static void GenerateReturn(Type type, IMessageSerializer serializer, List<MessageDescription> allDescriptions,MessageFormatType format)
        {
            serializer.NewLine("public void Return(ushort flags, short methodid,Int32 OpCode,NetStream stream,RpcSessionService service)");
            serializer.BeginBlock();
            serializer.NewLine("switch (methodid)");
            serializer.BeginBlock();
            MessageDescriptionGenerator mdgenerator = new MessageDescriptionGenerator();
            allDescriptions = allDescriptions.Where(description => description.IsRequest == false).ToList();

            allDescriptions.ForEach(description =>
            {
                serializer.NewLine(string.Format("// MessageId = {0}:", description.MessageId));
                serializer.NewLine(string.Format("case {0}:", description.MethodId));
                serializer.BeginBlock();
                if (format == MessageFormatType.Json)
                {
                    serializer.NewLine(string.Format("{0} response = {0}.ReadFromJson(stream);", description.Name));
                }
                else
                {
                    serializer.NewLine(string.Format("{0} response = new {0}();", description.Name));
                    serializer.NewLine("response.Read(stream);");
                }

                serializer.NewLine("object d = service.PopCallback(OpCode);");
                serializer.NewLine("if(null != d)");
                serializer.BeginBlock();
                {
                    string returnTypeName = description.Fields[0].IsGenericType() ? description.Fields[0].GenericTypeName : description.Fields[0].Type.Name;
                    serializer.NewLine(string.Format("Action<{0}> callback = d as Action<{0}>;", returnTypeName));
                    serializer.NewLine("if(null != callback)");
                    serializer.BeginBlock();
                    {
                        serializer.NewLine("callback(response.returnValue);");
                    }
                    serializer.EndBlock();
                }
                serializer.EndBlock();
                serializer.NewLine("break;");
                serializer.EndBlock();
            });
            serializer.NewLine("default:");
            serializer.BeginBlock();
            {
                serializer.NewLine("return;");
            }
            serializer.EndBlock();
            serializer.EndBlock();
            serializer.EndBlock();
        }
    }
}
